
#include "uffs/uffs_os.h"
#include "uffs/uffs_notifier.h"

void Uffs_Init_Lock(int *ppLock)
{
    //assert(ppLock);
    *ppLock = uffs_MutexCreate();
    //assert(*ppLock);
}

void Uffs_Lock(int pLock)
{
    uffs_MutexLock(pLock);
}

void Uffs_Unlock(int pLock)
{
    uffs_MutexUnlock(pLock);
}

void Uffs_Term_Lock(int pLock)
{
    uffs_MutexDestroy(pLock);
}

URET uffs_InitNotifier(struct uffs_DeviceSt *dev)
{
    if (dev == NULL) {
        return U_FAIL;
    }

    dev->pNotifier.m_pListHead = NULL;
    Uffs_Init_Lock(&(dev->pNotifier.m_pLock));
    return U_SUCC;
}

URET uffs_ReleaseNotifier(struct uffs_DeviceSt *dev)
{
    NotifierListNode *pNotifierNode;
    EventListNode *pHeadEventNode, *pEventNode;

    if (dev == NULL) {
        return U_FAIL;
    }

    while (dev->pNotifier.m_pListHead) {
        pNotifierNode = dev->pNotifier.m_pListHead;
        dev->pNotifier.m_pListHead = dev->pNotifier.m_pListHead->pNext;

        pHeadEventNode = pNotifierNode->pEvents;
        while (pHeadEventNode) {
            pEventNode = pHeadEventNode;
            pHeadEventNode = pHeadEventNode->pNext;
            uffs_MemFree(dev, pEventNode);
        }

        uffs_MemFree(dev, pNotifierNode);//check
    }

    if (dev->pNotifier.m_pLock) {
        Uffs_Term_Lock(dev->pNotifier.m_pLock);
    }

    return U_SUCC;
}

NotifierListNode * FindNode(
    /* [in] */ struct uffs_DeviceSt *dev,
    /* [in] */ TreeNode *pNode,
    /* [out] */ NotifierListNode **ppPreNode)
{
    NotifierListNode *pNotifierNode, *pPreNode;

    if (NULL == pNode)
        return NULL;

    if (NULL == dev->pNotifier.m_pListHead)
        return NULL;

    if (dev->pNotifier.m_pListHead->pNode == pNode) {
        pPreNode = NULL;
        pNotifierNode = dev->pNotifier.m_pListHead;
    }
    else {
        pPreNode = dev->pNotifier.m_pListHead;
        while (pPreNode->pNext) {
            if (pPreNode->pNext->pNode == pNode)
                break;
            pPreNode = pPreNode->pNext;
        }
        if (NULL == pPreNode->pNext)
            return NULL;
        else
            pNotifierNode = pPreNode->pNext;
    }

    if (ppPreNode)
        *ppPreNode = pPreNode;
    if (pNotifierNode->pNode != pNode) {
        uffs_Perror(UFFS_ERR_NORMAL, "pNotifierNode->pNode != pNode\n");
    }
    return pNotifierNode;
}

/*
 * Find the specified eventNode.
 */
EventListNode *FindEvent(
    /* [in] */ NotifierListNode *pNotifierNode,
    /* [in] */ int pEvent,
    /* [out] */ EventListNode **ppPreNode)
{
    EventListNode *pEventNode, *pPreEventNode;

    if ((NULL == pNotifierNode) || (NULL == pNotifierNode->pEvents))
        return NULL;

    if (pNotifierNode->pEvents->pEvent == pEvent) {
        pPreEventNode = NULL;
        pEventNode = pNotifierNode->pEvents;
    }
    else {
        pPreEventNode = pNotifierNode->pEvents;
        while (pPreEventNode->pNext) {
            if (pPreEventNode->pNext->pEvent == pEvent)
                break;
            pPreEventNode = pPreEventNode->pNext;
        }

        if (NULL == pPreEventNode->pNext)
            return NULL;
        else
            pEventNode = pPreEventNode->pNext;
    }

    if (ppPreNode)
        *ppPreNode = pPreEventNode;
    if (pEventNode->pEvent != pEvent) {
        uffs_Perror(UFFS_ERR_NORMAL, "pEventNode->pEvent != pEvent\n");
    }
    return pEventNode;
}

URET uffs_AddMonitor(struct uffs_DeviceSt *dev, TreeNode *pNode, int * pRetEvent)
{
    EventListNode *pEventNode = NULL;
    int pEvent = NULL;
    NotifierListNode *pNotifierNode;

    if (!dev || !pNode || !pRetEvent) {
        return -1;
    }

    *pRetEvent = NULL;

    Uffs_Lock(dev->pNotifier.m_pLock);

    /* Checkint out whether targeted node had been monitered */
    pNotifierNode = FindNode(dev, pNode, NULL);
    if (pNotifierNode) {
        pEventNode = (EventListNode*)uffs_MemAlloc(dev, sizeof(EventListNode));
        if (NULL == pEventNode)
            goto Exit1;

        pEvent = uffs_EventCreate(0, 0);
        if (NULL == pEvent)
            goto Exit1;
        /* Both filing system and application who invoke the routine
        * has to hold the reference of event.
        */
        uffs_EventAddRef(pEvent);
        uffs_EventAddRef(pEvent);
        pEventNode->pEvent = pEvent;
        pEventNode->pNext = pNotifierNode->pEvents;
        pNotifierNode->pEvents = pEventNode;
    }
    else {
        pNotifierNode = (NotifierListNode*)uffs_MemAlloc(dev, sizeof(NotifierListNode));
        if (NULL == pNotifierNode)
            goto Exit2;

        pEventNode = (EventListNode*)uffs_MemAlloc(dev, sizeof(EventListNode));
        if (NULL == pEventNode)
            goto Exit2;

        pEvent = uffs_EventCreate(0, 0);
        if (NULL == pEvent)
            goto Exit1;

        uffs_EventAddRef(pEvent);
        uffs_EventAddRef(pEvent);
        pEventNode->pEvent = pEvent;
        pEventNode->pNext = NULL;
        pNotifierNode->pNode = pNode;
        pNotifierNode->pEvents = pEventNode;
        pNotifierNode->pNext = dev->pNotifier.m_pListHead;
        dev->pNotifier.m_pListHead = pNotifierNode;
    }
    Uffs_Unlock(dev->pNotifier.m_pLock);
    *pRetEvent = pEvent;
    return 0;

Exit1:
    Uffs_Unlock(dev->pNotifier.m_pLock);
    if (pEventNode)
        uffs_MemFree(dev, pEventNode);
    return UENOMEM;

Exit2:
    Uffs_Unlock(dev->pNotifier.m_pLock);
    if (pNotifierNode)
        uffs_MemFree(dev, pNotifierNode);
    if (pEventNode)
        uffs_MemFree(dev, pEventNode);
    return UENOMEM;
}

URET uffs_RemoveMonitor(struct uffs_DeviceSt *dev, TreeNode *pNode, int pEvent)
{
    NotifierListNode *pNotifierNode, *pPreNotifierNode;
    EventListNode *pEventNode, *pPreEventNode;

    if (!pNode || !pEvent) {
        return -1;
    }

    Uffs_Lock(dev->pNotifier.m_pLock);

    pNotifierNode = FindNode(dev, pNode, &pPreNotifierNode);
    if (NULL == pNotifierNode)
        goto NoFind;

    pEventNode = FindEvent(pNotifierNode, pEvent, &pPreEventNode);
    if (NULL == pEventNode)
        goto NoFind;

    /* Pick out the event node from event list */
    if (NULL == pPreEventNode)
        pNotifierNode->pEvents = pEventNode->pNext;
    else
        pPreEventNode->pNext = pEventNode->pNext;

    uffs_EventDestroy(pEventNode->pEvent);
    uffs_MemFree(dev, pEventNode);

    /* Remove the notifier node from notifier node list if no more event
     * in this node.
     */
    if (NULL == pNotifierNode->pEvents) {
        if (NULL == pPreNotifierNode)
            dev->pNotifier.m_pListHead = pNotifierNode->pNext;
        else
            pPreNotifierNode->pNext = pNotifierNode->pNext;

        uffs_MemFree(dev, pNotifierNode);
    }
    Uffs_Unlock(dev->pNotifier.m_pLock);
    return 0;

NoFind:
    Uffs_Unlock(dev->pNotifier.m_pLock);
    return U_FAIL;
}

URET uffs_NotifyMonitor(struct uffs_DeviceSt *dev, TreeNode * pChangeNode)
{
    NotifierListNode *pNotifierNode;
    EventListNode *pEventNode;

    Uffs_Lock(dev->pNotifier.m_pLock);

    if (NULL == dev->pNotifier.m_pListHead)
        goto Exit;

    pNotifierNode = FindNode(dev, pChangeNode, NULL);
    if (NULL == pNotifierNode)
        goto Exit;

    pEventNode = pNotifierNode->pEvents;
    if (pEventNode == NULL) {
        uffs_Perror(UFFS_ERR_NORMAL, "pEventNode is NULL\n");
    }

    while (pEventNode) {
        if (uffs_EventNotify(pEventNode->pEvent) != 0) {
            uffs_Perror(UFFS_ERR_NORMAL, "Event->notify failed\n");
            goto Exit;
        }
        pEventNode = pEventNode->pNext;
    }

    Uffs_Unlock(dev->pNotifier.m_pLock);
    return U_SUCC;

Exit:
    Uffs_Unlock(dev->pNotifier.m_pLock);
    return U_FAIL;
}

/*
 * Notify all events whose related node is dirty
 */
//int uffs_NotifyAllDirty(uffs_Device *dev)
//{
//    NotifierListNode *pNotifierNode;
//    EventListNode *pEventNode;
//
//    Uffs_Lock(dev->pNotifier.m_pLock);
//
//    pNotifierNode = dev->pNotifier.m_pListHead;
//
//    while (pNotifierNode) {
//        if (pNotifierNode->pNode == ->n_flags & N_DIRTY) {
//            pEventNode = pNotifierNode->pEvents;
//            assert(pEventNode);
//            while (pEventNode) {
//                assert(pEventNode->pEvent);
//                uffs_EventNotify(pEventNode->pEvent);
//                pEventNode = pEventNode->pNext;
//            }
//            pNotifierNode->pNode->n_flags &= ~N_DIRTY;
//        }
//        pNotifierNode = pNotifierNode->pNext;
//    }
//
//    Uffs_Unlock(dev->pNotifier.m_pLock);
//    return 0;
//}
